﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
using SharpSoft.Data.DAOS;

namespace SharpSoft.Web
{
    public abstract class FilesController : BaseController
    {
        private readonly string folderPath;


        protected FilesController(IOptions<AppConfig> appconfig, IHttpContextAccessor p_accessor, IWebHostEnvironment p_env, DatabaseService p_databaseService) : base(appconfig, p_accessor, p_env, p_databaseService)
        {
            if (string.IsNullOrWhiteSpace(this.FilesFolder()))
            {
                throw new Exception("必须指定有效的文件夹名称。");
            }
            folderPath = MapAppPath(this.FilesFolder());
            if (!Directory.Exists(folderPath))
            {
                Directory.CreateDirectory(folderPath);
            }
        }

        /// <summary>
        /// 在派生类中实现，设定文件储存路径
        /// </summary>
        /// <returns></returns>
        protected abstract string FilesFolder();

        /// <summary>
        /// 删除已经存在的文件
        /// </summary>
        /// <param name="files"></param>
        private void DelExistsFiles(string[] files)
        {
            try
            {
                foreach (var item in files)
                {
                    var f = Path.Combine(folderPath, item);
                    if (System.IO.File.Exists(f))
                    {
                        System.IO.File.Delete(f);
                    }
                }
            }
            catch (Exception)
            {

            }

        }

        /// <summary>
        /// 上传文件
        /// </summary>
        /// <returns>返回{filekey:fileid}</returns> 
        protected ApiResult _UploadFile(int? folderid = null, string tag = null, string remark = null)
        {
            var models = this.SaveFiles(this.Request.Form.Files, folderid, tag, remark);
            return Success(models);
        }
        /// <summary>
        /// 保存文件
        /// </summary>
        /// <param name="files"></param>
        /// <param name="folderid"></param>
        /// <param name="tag"></param>
        /// <param name="remark"></param>
        /// <returns></returns>
        protected ServerFileModel[] SaveFiles(IEnumerable<IFormFile> files, int? folderid = null, string tag = null, string remark = null)
        {
            var filecount = files.Count();
            var dal = this.dao<ServerFileDAO>();
            if (files == null || filecount == 0)
            {
                throw new Exception("未上传任何文件。");
            }
            short i = 0;
            var ran = new Random();
            var names = new string[filecount];
            var onames = new string[filecount];
            var hashes = new string[filecount];

            var ids = new List<int>(names.Length);
            var tran = dal.Database.BeginTransaction();
            try
            {
                foreach (var file in files)
                {
                    var oname = file.FileName;//原文件名
                    if (oname != null)
                    {//egret等部分浏览器会提供原始文件的全路径，只提取文件名部分。
                        oname = System.IO.Path.GetFileName(oname);
                    }
                    onames[i] = oname;
                    var tempname = Path.Combine(folderPath, $"temp_{Guid.NewGuid().ToString()}");
                    using (var stream = file.OpenReadStream())
                    {
                        using (var fs = new FileStream(tempname, FileMode.Create, FileAccess.Write))
                        {
                            byte[] buf = new byte[2048];
                            while (true)
                            {
                                var count = stream.Read(buf, 0, buf.Length);
                                if (count == 0)
                                {
                                    break;
                                }
                                else
                                {
                                    fs.Write(buf, 0, count);
                                }
                            }
                        }
                        using (var fs = new FileStream(tempname, FileMode.Open, FileAccess.Read))
                        {
                            var hash = SharpSoft.Security.MD5Hash(fs);
                            hashes[i] = hash;
                        }
                    }

                    if (dal.ExistsFileHash(folderid, hashes[i], tag, out int? exists_fid))
                    {
                        ids.Add(exists_fid.Value);//已存在的文件同样反馈到列表
                        System.IO.File.Delete(tempname);
                        continue;
                        //throw new Exception($"服务器上已存在相同用的文件:{onames[i]}");
                    }

                    var timedata = BitConverter.GetBytes(DateTime.Now.ToBinary());
                    var nname = new Guid(this.UserId, i, (short)ran.Next(short.MinValue, short.MaxValue), timedata).ToString();//生成新的文件名
                    names[i] = nname;
                    var fid = dal.InsertServerFile(this.UserId, folderid, names[i], onames[i], hashes[i], tag, remark);
                    ids.Add(fid);

                    getPageFolder(fid, out string level1, out string level2);//获取分页目录名称

                    var realfolder = System.IO.Path.Combine(folderPath, level1, level2);
                    if (!System.IO.Directory.Exists(realfolder))
                    {
                        System.IO.Directory.CreateDirectory(realfolder);
                    }
                    System.IO.File.Move(tempname, System.IO.Path.Combine(realfolder, nname));//将临时文件移到实际位置

                    i++;
                }
                tran.Commit();
            }
            catch (Exception ex)
            {
                tran.Rollback();
                DelExistsFiles(names);
                throw ex;
            }
            try
            {
                ServerFileModel[] fmodels = dal.GetServerFiles(ids.ToArray());
                return fmodels;
            }
            catch (Exception ex)
            {
                return null;
            }

        }

        /// <summary>
        /// 获取文件分页目录
        /// </summary>
        /// <param name="fid"></param>
        /// <returns></returns>
        protected void getPageFolder(int fid, out string level1Name, out string level2Name)
        {
            var level1Count = 100000;
            var level2Count = 500;
            level1Name = (((fid / level1Count) + 1) * level1Count).ToString();//第一级目录名称
            level2Name = (((fid / level2Count) + 1) * level2Count).ToString();//第二级目录名称 
        }
        /// <summary>
        /// 下载原文件
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        protected IActionResult _DownloadFile(string name)
        {
            var dal = this.dao<ServerFileDAO>();
            var filemodel = dal.GetServerFile(name);
            if (filemodel == null)
            {
                return StatusCode(404);
            }
            return File(new FileStream(Path.Combine(folderPath, name), FileMode.Open, FileAccess.Read), "application/octet-stream", filemodel.originalName, true);
        }
        /// <summary>
        /// 获取文件列表
        /// </summary>
        /// <param name="scopes">用于识别文件的查找范围</param>
        /// <param name="search"></param>
        /// <returns></returns> 
        [HttpGet]
        public virtual ApiResult GetFileList([FromQuery] int pageindex, [FromQuery] int count, [FromQuery] string scopes, [FromBody] FileSearch search)
        {
            string condition = "";
            List<string> cons = new List<string>();
            if (search != null)
            {
                if (search.starttime.HasValue)
                {
                    cons.Add("`uploadTime`>=@starttime");
                }
                if (search.endtime.HasValue)
                {
                    cons.Add("`uploadTime`>=@endtime");
                }
                if (search.folderid.HasValue)
                {
                    cons.Add("`folderid`=@folderid");
                }
                if (!string.IsNullOrEmpty(search.tag))
                {
                    cons.Add("`tag`=@tag");
                }
                if (!(search.remark == null))
                {
                    cons.Add("`remark` LIKE @search");
                }
                if (!(search.filename == null))
                {
                    cons.Add("`originalName` LIKE @filename");
                }
                if (search.extensions != null && search.extensions.Length > 0)
                {
                    List<string> l = new List<string>();
                    foreach (var item in search.extensions)
                    {
                        l.Add($"`extensionName` = '{item.Replace("'", "''")}'");
                    }
                    cons.Add("(" + string.Join(" OR ", l.ToArray()) + ")");
                }
                condition = string.Join(" AND ", cons.ToArray());
            }
            var cond1 = this.getFileListCondition(scopes);
            if (!string.IsNullOrWhiteSpace(cond1))
            {
                condition = cond1 + " AND " + condition;
            }
            var fdal = this.dao<ServerFileDAO>();
            var data = fdal.GetServerFiles(condition, search, pageindex, count);

            return Success(data);
        }

        /// <summary>
        /// 在派生类中实现，为指定的获取文件的范围生成相应的SQL查询条件
        /// </summary>
        /// <param name="scopes"></param>
        /// <returns></returns>
        protected virtual string getFileListCondition(string scopes) => null;



        /// <summary>
        /// 获取图片文件缩略图
        /// </summary>
        /// <param name="name">文件名称</param>
        /// <param name="s">缩略图尺寸</param>
        /// <returns></returns>
        protected virtual IActionResult GetThumbnail(string name, int s = 300)
        {
            if (string.IsNullOrEmpty(name) || s <= 0 || name.ToLower() == "null" || name.ToLower() == "undefined")
            {
                var stream = ImageHelper.TransparencyImage(1, 1);
                return File(stream, "image/png", "tran.png");
            }

            if (string.IsNullOrEmpty(name))
            {
                return StatusCode(404);
            }
            var i = name.IndexOf('_');
            if (i < 0)
            {
                return StatusCode(404);
            }
            var fidstr = name.Substring(0, i);
            if (int.TryParse(fidstr, out int fid))
            {
                var filename = name.SubstringUnsafe(i + 1, name.Length - i - 1);
                getPageFolder(fid, out string level1, out string level2);//获取分页目录名称 
                var realfolder = System.IO.Path.Combine(folderPath, level1, level2);
                var fname = System.IO.Path.Combine(realfolder, filename);
                if (!System.IO.File.Exists(fname))
                {//原始文件不存在
                    return StatusCode(404);
                }
                var thumbFolder = Path.Combine(realfolder, "Thumbnail", s.ToString());
                var thumbFile = Path.Combine(thumbFolder, filename);
                if (!System.IO.File.Exists(thumbFile))
                { //缩略文件不存在
                    if (!Directory.Exists(thumbFolder))
                    {
                        Directory.CreateDirectory(thumbFolder);
                    }
                    try
                    {
                        using (var img = System.Drawing.Bitmap.FromFile(fname))
                        {
                            using (var thumbBmp = ImageHelper.SizeImage(img, s, s, null))
                            {
                                thumbBmp.Save(thumbFile, System.Drawing.Imaging.ImageFormat.Png);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        throw new Exception("生成缩略图失败。（error when processing the image.）", ex);
                    }

                }
                return File(System.IO.File.OpenRead(thumbFile), "image/*", "web_img_thum");
            }
            else
            {
                return StatusCode(404);
            }
        }

        /// <summary>
        /// 获取原图
        /// </summary>
        /// <param name="name"></param>
        /// <param name="s"></param>
        /// <returns></returns> 
        [HttpGet]
        public virtual IActionResult GetImage(string name)
        {
            if (string.IsNullOrEmpty(name) || name.ToLower() == "null" || name.ToLower() == "undefined")
            {
                var stream = ImageHelper.TransparencyImage(1, 1);
                return File(stream, "image/png", "tran.png");
            }

            if (string.IsNullOrEmpty(name))
            {
                return StatusCode(404);
            }
            var i = name.IndexOf('_');
            if (i < 0)
            {
                return StatusCode(404);
            }
            var fidstr = name.Substring(0, i);
            if (int.TryParse(fidstr, out int fid))
            {
                var filename = name.SubstringUnsafe(i + 1, name.Length - i - 1);
                getPageFolder(fid, out string level1, out string level2);//获取分页目录名称 
                var realfolder = System.IO.Path.Combine(folderPath, level1, level2);
                var fname = System.IO.Path.Combine(realfolder, filename);
                if (!System.IO.File.Exists(fname))
                {//原始文件不存在
                    return StatusCode(404);
                }

                return File(System.IO.File.OpenRead(fname), "image/*", "web_img_sou");
            }
            else
            {
                return StatusCode(404);
            }
        }
    }

    /// <summary>
    /// 文件搜索参数
    /// </summary>
    public class FileSearch
    {
        /// <summary>
        /// 搜索起始上传时间
        /// </summary>
        public DateTime? starttime { get; set; }
        /// <summary>
        /// 搜索截至上传时间
        /// </summary>
        public DateTime? endtime { get; set; }
        /// <summary>
        /// 搜索备注包含
        /// </summary>
        public Data.LikeParameter remark { get; set; }
        /// <summary>
        /// 搜索文件名包含
        /// </summary>
        public Data.LikeParameter filename { get; set; }
        /// <summary>
        /// 搜索后缀名（完全匹配）
        /// </summary>
        public string[] extensions { get; set; }
        /// <summary>
        /// 搜索标签（完全匹配）
        /// </summary>
        public string tag { get; set; }
        /// <summary>
        /// 查找的文件夹
        /// </summary>
        public int? folderid { get; set; }
    }
}
